import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:dio/adapter.dart';
import 'package:flutter/foundation.dart';
import '../index.dart';

class HttpGo {
  static const String clientId = 'gxsaas-platform-app'; // 客户端id
  static const String clientSecret = 'gxsaas_app_secret'; // 客户端密钥
  static const String authorizationKey = 'gxsaas-Auth';
  static const String devApiDomain =
      'http://light.saas.sit.guoxintianyu.com/api';
  static const String releaseApiDomain =
      'http://light.saas.pro.guoxintianyu.com/api';

  static const String proxyUrl = '192.168.90.86:8888';
  static const String kContentTypeHeaderForm =
      "application/x-www-form-urlencoded";
  static const String kContentTypeHeaderJson = "application/json";
  static const int connectTimeout = 30 * 1000;
  static const int sendTimeout = 600 * 1000;
  static const int receiveTimeout = 30 * 1000;

  static const int kPageSize = 10;

  //主要dio需要添加刷新token插件
  static Dio dio = createDio();

  //刷新token或二次请求数据用dio
  static Dio reserveDio = createDio();

  // DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  // AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
  // print('Running on ${androidInfo.model}');  // e.g. "Moto G (4)"
  //
  // IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
  // print('Running on ${iosInfo.utsname.machine}');  // e.g. "iPod7,1"
  //
  // WebBrowserInfo webBrowserInfo = await deviceInfo.webBrowserInfo;
  // print('Running on ${webBrowserInfo.userAgent}');

  static Map<String, dynamic> headers() {
    // DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
    // final map = await deviceInfo.deviceInfo;
    // Debug.info(map.toString());
    // AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
    // print('Running on ${androidInfo.model}');  // e.g. "Moto G (4)"
    //
    // IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
    // print('Running on ${iosInfo.utsname.machine}');  // e.g. "iPod7,1"
    //
    // WebBrowserInfo webBrowserInfo = await deviceInfo.webBrowserInfo;
    // print('Running on ${webBrowserInfo.userAgent}');
    return {
      HttpHeaders.contentTypeHeader: kContentTypeHeaderForm,
      HttpHeaders.userAgentHeader: Global.deviceInfo,
      "User-Type": "app"
    };
  }

  static createDio() {
    return Dio(BaseOptions(
      baseUrl: Global.isRelease ? releaseApiDomain : Global.profile.apiDomain,
      headers: headers(),
    ));
  }

  static void init() {
    dio.interceptors.add(RefreshTokenInterceptor());
    initDio(dio);
    initDio(reserveDio);
    //主要dio需要添加刷新token插件
  }

  static void initDio(Dio dio) {
    // 添加缓存插件
    dio.interceptors.add(Global.netCache);
    dio.options.connectTimeout = connectTimeout;
    dio.options.sendTimeout = sendTimeout; //30s
    dio.options.receiveTimeout = receiveTimeout; //60s
    // 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验
    if (!Global.isRelease && Global.profile.isUseProxy) {
      if (!kIsWeb) {
        (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
            (client) {
          // // 设置代理抓包，调试用
          client.findProxy = (uri) {
            // localProxyIPAddress 和 localProxyPort 是在外部被忽略文件中定义, 方便各个开发者自行更改.
            return "PROXY " + proxyUrl;
          };

          //代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验
          if (Global.profile.apiDomain.toLowerCase().startsWith("https")) {
            client.badCertificateCallback =
                (X509Certificate cert, String host, int port) => true;
          }
        };
      }
    }
  }

  late BuildContext context;
  late Options options;

  // 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时
  // 打开一个新路由，而打开新路由需要context信息。
  HttpGo(this.context) {
    options = Options(
        extra: {"context": context},
        headers: headers(),
        sendTimeout: sendTimeout,
        //30s
        receiveTimeout: receiveTimeout //60s
        );
  }

  /// Handy method to make http GET request, which is a alias of  [dio.fetch(RequestOptions)].
  Future<Response> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.get(
        path,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }

  /// Handy method to make http POST request, which is a alias of  [dio.fetch(RequestOptions)].
  Future<Response> post<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.post(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }

  /// Handy method to make http PUT request, which is a alias of  [dio.fetch(RequestOptions)].
  Future<Response> put<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.put(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }

  /// Handy method to make http HEAD request, which is a alias of [dio.fetch(RequestOptions)].
  Future<Response> head<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.head(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }

  /// Handy method to make http DELETE request, which is a alias of  [dio.fetch(RequestOptions)].
  Future<Response> delete<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.delete(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }

  /// Handy method to make http PATCH request, which is a alias of  [dio.fetch(RequestOptions)].
  Future<Response> patch<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.patch(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }

  Future<Response> download(
    String urlPath,
    savePath, {
    ProgressCallback? onReceiveProgress,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    bool deleteOnError = true,
    String lengthHeader = Headers.contentLengthHeader,
    data,
    Options? options,
  }) async {
    return await dio.download(urlPath, savePath,
        onReceiveProgress: onReceiveProgress,
        queryParameters: queryParameters,
        cancelToken: cancelToken,
        deleteOnError: deleteOnError,
        lengthHeader: lengthHeader,
        data: data,
        options: options);
  }

  /// Make http request with options.
  ///
  /// [path] The url path.
  /// [data] The request data
  /// [options] The request options.
  Future<Response> request<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
    DataParseCallback<T>? dataParseCallback,
  }) async {
    try {
      Response response = await dio.request(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      response.data = ResponseData.fromJson(response.data,
          dataParseCallback: dataParseCallback);
      return response;
    } on DioError catch (e) {
      if (e.type == DioErrorType.response) {
        if (e.response != null && e.response?.data != null) {
          e.response?.data = ResponseData.fromJson(e.response?.data,
              dataParseCallback: dataParseCallback);
          return e.response!;
        }
        Debug.info(e.toString());
        rethrow;
      } else {
        Debug.info(e.toString());
        rethrow;
      }
    }
  }
}
